// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build ignore 

// mkpreempt为每个
// 体系结构生成异步抢占函数。
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
)

// 复制自cmd/compile/internal/ssa/gen/*Ops。转到

var regNames386 = []string{
	"AX",
	"CX",
	"DX",
	"BX",
	"SP",
	"BP",
	"SI",
	"DI",
	"X0",
	"X1",
	"X2",
	"X3",
	"X4",
	"X5",
	"X6",
	"X7",
}

var regNamesAMD64 = []string{
	"AX",
	"CX",
	"DX",
	"BX",
	"SP",
	"BP",
	"SI",
	"DI",
	"R8",
	"R9",
	"R10",
	"R11",
	"R12",
	"R13",
	"R14",
	"R15",
	"X0",
	"X1",
	"X2",
	"X3",
	"X4",
	"X5",
	"X6",
	"X7",
	"X8",
	"X9",
	"X10",
	"X11",
	"X12",
	"X13",
	"X14",
	"X15",
}

var out io.Writer

var arches = map[string]func(){
	"386":     gen386,
	"amd64":   genAMD64,
	"arm":     genARM,
	"arm64":   genARM64,
	"mips64x": func() { genMIPS(true) },
	"mipsx":   func() { genMIPS(false) },
	"ppc64x":  genPPC64,
	"riscv64": genRISCV64,
	"s390x":   genS390X,
	"wasm":    genWasm,
}
var beLe = map[string]bool{"mips64x": true, "mipsx": true, "ppc64x": true}

func main() {
	flag.Parse()
	if flag.NArg() > 0 {
		out = os.Stdout
		for _, arch := range flag.Args() {
			gen, ok := arches[arch]
			if !ok {
				log.Fatalf("unknown arch %s", arch)
			}
			header(arch)
			gen()
		}
		return
	}

	for arch, gen := range arches {
		f, err := os.Create(fmt.Sprintf("preempt_%s.s", arch))
		if err != nil {
			log.Fatal(err)
		}
		out = f
		header(arch)
		gen()
		if err := f.Close(); err != nil {
			log.Fatal(err)
		}
	}
}

func header(arch string) {
	fmt.Fprintf(out, "// 由mkpreempt生成的代码。去不要编辑。\n\n“）
	if beLe[arch] {
		base := arch[:len(arch)-1]
		fmt.Fprintf(out, "// go:build%s | |%sle\n”，base，base）
	}
	fmt.Fprintf(out, "#include \"go_asm.h\"\n")
	fmt.Fprintf(out, "#include \"textflag.h\"\n\n")
	fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n")
}

func p(f string, args ...any) {
	fmted := fmt.Sprintf(f, args...)
	fmt.Fprintf(out, "\t%s\n", strings.ReplaceAll(fmted, "\n", "\n\t"))
}

func label(l string) {
	fmt.Fprintf(out, "%s\n", l)
}

type layout struct {
	stack int
	regs  []regPos
	sp    string // 堆栈指针寄存器
}

type regPos struct {
	pos int

	op  string
	reg string

	// 如果此寄存器需要特殊保存和恢复，这些
	// 为堆栈
	// 偏移量提供带有%d占位符的操作。
	save, restore string
}

func (l *layout) add(op, reg string, size int) {
	l.regs = append(l.regs, regPos{op: op, reg: reg, pos: l.stack})
	l.stack += size
}

func (l *layout) addSpecial(save, restore string, size int) {
	l.regs = append(l.regs, regPos{save: save, restore: restore, pos: l.stack})
	l.stack += size
}

func (l *layout) save() {
	for _, reg := range l.regs {
		if reg.save != "" {
			p(reg.save, reg.pos)
		} else {
			p("%s %s, %d(%s)", reg.op, reg.reg, reg.pos, l.sp)
		}
	}
}

func (l *layout) restore() {
	for i := len(l.regs) - 1; i >= 0; i-- {
		reg := l.regs[i]
		if reg.restore != "" {
			p(reg.restore, reg.pos)
		} else {
			p("%s %d(%s), %s", reg.op, reg.pos, l.sp, reg.reg)
		}
	}
}

func gen386() {
	p("PUSHFL")
	// 保存通用寄存器。
	var l = layout{sp: "SP"}
	for _, reg := range regNames386 {
		if reg == "SP" || strings.HasPrefix(reg, "X") {
			continue
		}
		l.add("MOVL", reg, 4)
	}

	softfloat := "GO386_softfloat"

	// 仅在支持时保存SSE状态。
	lSSE := layout{stack: l.stack, sp: "SP"}
	for i := 0; i < 8; i++ {
		lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16)
	}

	p("ADJSP $%d", lSSE.stack)
	p("NOP SP")
	l.save()
	p("#ifndef %s", softfloat)
	lSSE.save()
	p("#endif")
	p("CALL ·asyncPreempt2(SB)")
	p("#ifndef %s", softfloat)
	lSSE.restore()
	p("#endif")
	l.restore()
	p("ADJSP $%d", -lSSE.stack)

	p("POPFL")
	p("RET")
}

func genAMD64() {
	// 分配堆栈偏移量。
	var l = layout{sp: "SP"}
	for _, reg := range regNamesAMD64 {
		if reg == "SP" || reg == "BP" {
			continue
		}
		if !strings.HasPrefix(reg, "X") {
			l.add("MOVQ", reg, 8)
		}
	}
	lSSE := layout{stack: l.stack, sp: "SP"}
	for _, reg := range regNamesAMD64 {
		if strings.HasPrefix(reg, "X") {
			lSSE.add("MOVUPS", reg, 16)
		}
	}

	// TODO:MXCSR寄存器？

	p("PUSHQ BP")
	p("MOVQ SP, BP")
	p("// 在关闭前保存标志）
	p("PUSHFQ")
	p("// obj不了解SP上的ADD/SUB，但了解ADJSP）
	p("ADJSP $%d", lSSE.stack)
	p("// 但兽医不知道ADJSP，所以抑制vet堆栈检查）
	p("NOP SP")

	l.save()

	// 显然，达尔文内核中的信号处理代码路径使
	// Y寄存器的高位处于脏状态，这会导致
	// 许多SSE操作（128位或更窄）变得更慢。
	// 清除高位以获得干净状态。见第37174期。
	// 这里很安全，因为Go代码不使用Y寄存器的高位。
	p("#ifdef GOOS_darwin")
	p("CMPB internal∕cpu·X86+const_offsetX86HasAVX(SB), $0")
	p("JE 2(PC)")
	p("VZEROUPPER")
	p("#endif")

	lSSE.save()
	p("CALL ·asyncPreempt2(SB)")
	lSSE.restore()
	l.restore()
	p("ADJSP $%d", -lSSE.stack)
	p("POPFQ")
	p("POPQ BP")
	p("RET")
}

func genARM() {
	// 添加整数寄存器R0-R12。
	// R13（SP）、R14（LR）、R15（PC）是特殊的，不保存在这里。
	var l = layout{sp: "R13", stack: 4} // 添加LR插槽
	for i := 0; i <= 12; i++ {
		reg := fmt.Sprintf("R%d", i)
		if i == 10 {
			continue // R10是g寄存器，无需保存/恢复
		}
		l.add("MOVW", reg, 4)
	}
	// 添加标志寄存器。
	l.addSpecial(
		"MOVW CPSR, R0\nMOVW R0, %d(R13)",
		"MOVW %d(R13), R0\nMOVW R0, CPSR",
		4)

	// 添加浮点寄存器F0-F15和标志寄存器。
	var lfp = layout{stack: l.stack, sp: "R13"}
	lfp.addSpecial(
		"MOVW FPCR, R0\nMOVW R0, %d(R13)",
		"MOVW %d(R13), R0\nMOVW R0, FPCR",
		4)
	for i := 0; i <= 15; i++ {
		reg := fmt.Sprintf("F%d", i)
		lfp.add("MOVD", reg, 8)
	}

	p("MOVW.W R14, -%d(R13)", lfp.stack) // 分配帧，保存LR 
	l.save()
	p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp") // 测试goarm，如果goarm=5，则跳过FP寄存器。
	lfp.save()
	label("nofp:")
	p("CALL ·asyncPreempt2(SB)")
	p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp2") // 测试goarm，如果goarm=5，则跳过FP寄存器。
	lfp.restore()
	label("nofp2:")
	l.restore()

	p("MOVW %d(R13), R14", lfp.stack)     // sigctxt。pushCall在堆栈上推送LR，恢复它
	p("MOVW.P %d(R13), R15", lfp.stack+4) // 加载PC，pop帧（包括SigcText.pushCall推送的空间）
	p("UNDEF")                            // 不应该到这里
}

func genARM64() {
	// 添加整数寄存器R0-R26 
	// R27（REGTMP），R28（g），R29（FP），R30（LR），R31（SP）是特殊的
	var l = layout{sp: "RSP", stack: 8} // 添加插槽以保存中断指令的PC 
	for i := 0; i <= 26; i++ {
		if i == 18 {
			continue // R18未使用，跳过
		}
		reg := fmt.Sprintf("R%d", i)
		l.add("MOVD", reg, 8)
	}
	// 添加标志寄存器。
	l.addSpecial(
		"MOVD NZCV, R0\nMOVD R0, %d(RSP)",
		"MOVD %d(RSP), R0\nMOVD R0, NZCV",
		8)
	l.addSpecial(
		"MOVD FPSR, R0\nMOVD R0, %d(RSP)",
		"MOVD %d(RSP), R0\nMOVD R0, FPSR",
		8)
	// 待办事项：FPCR？我认为我们不会改变它，所以不需要存钱。
	// 添加浮点寄存器F0-F31。
	for i := 0; i <= 31; i++ {
		reg := fmt.Sprintf("F%d", i)
		l.add("FMOVD", reg, 8)
	}
	if l.stack%16 != 0 {
		l.stack += 8 // SP需要16字节对齐
	}

	// 分配帧，保存中断指令的PC（在LR中）
	p("MOVD R30, %d(RSP)", -l.stack)
	p("SUB $%d, RSP", l.stack)
	p("#ifdef GOOS_linux")
	p("MOVD R29, -8(RSP)") // 保存帧指针（仅在Linux上使用）
	p("SUB $8, RSP, R29")  // 在iOS上设置新的帧指针
	p("#endif")
	// ，递减SP后再次保存LR。我们在G堆栈上运行信号处理程序（因为它不支持sigaltstack），因此SP下的任何写入操作都可能被删除。
	p("#ifdef GOOS_ios")
	p("MOVD R30, (RSP)")
	p("#endif")

	l.save()
	p("CALL ·asyncPreempt2(SB)")
	l.restore()

	p("MOVD %d(RSP), R30", l.stack) // sigctxt。pushCall已在堆栈上推送LR（在中断时），将其还原为
	p("#ifdef GOOS_linux")
	p("MOVD -8(RSP), R29") // 还原帧指针
	p("#endif")
	p("MOVD (RSP), R27")          // 将PC加载到REGTMP 
	p("ADD $%d, RSP", l.stack+16) // pop帧（包括sigctxt.pushCall推送的空间）
	p("JMP (R27)")
}

func genMIPS(_64bit bool) {
	mov := "MOVW"
	movf := "MOVF"
	add := "ADD"
	sub := "SUB"
	r28 := "R28"
	regsize := 4
	softfloat := "GOMIPS_softfloat"
	if _64bit {
		mov = "MOVV"
		movf = "MOVD"
		add = "ADDV"
		sub = "SUBV"
		r28 = "RSB"
		regsize = 8
		softfloat = "GOMIPS64_softfloat"
	}

	// 添加整数寄存器R1-R22、R24-R25、R28 
	// R0（零）、R23（REGTMP）、R29（SP）、R30（g）、R31（LR）是特殊的，
	// 未保存在此处。R26和R27由内核保留，不使用。
	var l = layout{sp: "R29", stack: regsize} // 添加插槽以保存中断指令的PC（在LR中）
	for i := 1; i <= 25; i++ {
		if i == 23 {
			continue // R23是REGTMP 
		}
		reg := fmt.Sprintf("R%d", i)
		l.add(mov, reg, regsize)
	}
	l.add(mov, r28, regsize)
	l.addSpecial(
		mov+" HI, R1\n"+mov+" R1, %d(R29)",
		mov+" %d(R29), R1\n"+mov+" R1, HI",
		regsize)
	l.addSpecial(
		mov+" LO, R1\n"+mov+" R1, %d(R29)",
		mov+" %d(R29), R1\n"+mov+" R1, LO",
		regsize)

	// 添加浮点控制/状态寄存器FCR31（FCR0-FCR30不相关）
	var lfp = layout{sp: "R29", stack: l.stack}
	lfp.addSpecial(
		mov+" FCR31, R1\n"+mov+" R1, %d(R29)",
		mov+" %d(R29), R1\n"+mov+" R1, FCR31",
		regsize)
	// 添加浮点寄存器F0-F31。
	for i := 0; i <= 31; i++ {
		reg := fmt.Sprintf("F%d", i)
		lfp.add(movf, reg, regsize)
	}

	// 分配帧，保存中断指令的PC（在LR中）
	p(mov+" R31, -%d(R29)", lfp.stack)
	p(sub+" $%d, R29", lfp.stack)

	l.save()
	p("#ifndef %s", softfloat)
	lfp.save()
	p("#endif")
	p("CALL ·asyncPreempt2(SB)")
	p("#ifndef %s", softfloat)
	lfp.restore()
	p("#endif")
	l.restore()

	p(mov+" %d(R29), R31", lfp.stack)     // sigctxt。pushCall已在堆栈上推送LR（在中断时），将其恢复为
	p(mov + " (R29), R23")                // 将PC加载到REGTMP 
	p(add+" $%d, R29", lfp.stack+regsize) // pop帧（包括sigctxt.pushCall推送的空间）
	p("JMP (R23)")
}

func genPPC64() {
	// 添加整数寄存器R3-R29 
	// R0（零）、R1（SP）、R30（g）是特殊的，不保存在这里。
	// R2（PIC模式下的TOC指针）、R12（PIC模式下的函数输入地址）已保存在sigctxt中。推电话。
	// R31（REGTMP）将手动保存。
	var l = layout{sp: "R1", stack: 32 + 8} // PPC64上的MinFrameSize，加上一个用于保存R31的单词
	for i := 3; i <= 29; i++ {
		if i == 12 || i == 13 {
			// R12已保存在sigctxt中。推电话。
			// R13是TLS指针，不被Go代码使用。我们不能恢复它，否则如果我们停在一个不同的线程上恢复，我们会弄乱TLS地址。
			continue
		}
		reg := fmt.Sprintf("R%d", i)
		l.add("MOVD", reg, 8)
	}
	l.addSpecial(
		"MOVW CR, R31\nMOVW R31, %d(R1)",
		"MOVW %d(R1), R31\nMOVFL R31, $0xff", // 这是MOVW R31，CR 
		8)                                    // CR是4字节宽，但只需保持对齐
	l.addSpecial(
		"MOVD XER, R31\nMOVD R31, %d(R1)",
		"MOVD %d(R1), R31\nMOVD R31, XER",
		8)
	// 添加浮点寄存器F0-F31即可。
	for i := 0; i <= 31; i++ {
		reg := fmt.Sprintf("F%d", i)
		l.add("FMOVD", reg, 8)
	}
	// 添加浮点控制/状态寄存器FPSCR。
	l.addSpecial(
		"MOVFL FPSCR, F0\nFMOVD F0, %d(R1)",
		"FMOVD %d(R1), F0\nMOVFL F0, FPSCR",
		8)

	p("MOVD R31, -%d(R1)", l.stack-32) // 首先保存R31，我们将使用R31保存LR 
	p("MOVD LR, R31")
	p("MOVDU R31, -%d(R1)", l.stack) // 分配帧，保存中断指令的PC（在LR中）

	l.save()
	p("CALL ·asyncPreempt2(SB)")
	l.restore()

	p("MOVD %d(R1), R31", l.stack) // sigctxt。pushCall已在堆栈上推送LR、R2、R12（在中断时），将它们还原为
	p("MOVD R31, LR")
	p("MOVD %d(R1), R2", l.stack+8)
	p("MOVD %d(R1), R12", l.stack+16)
	p("MOVD (R1), R31") // 将PC加载到CTR 
	p("MOVD R31, CTR")
	p("MOVD 32(R1), R31")        // 还原R31 
	p("ADD $%d, R1", l.stack+32) // 弹出帧（包括sigctxt.pushCall推送的空间）
	p("JMP (CTR)")
}

func genRISCV64() {
	// X0（零）、X1（LR）、X2（SP）、X3（GP）、X4（TP）、X27（g）、X31（TMP）是特殊的。
	var l = layout{sp: "X2", stack: 8}

	// 添加整数寄存器（X5-X26、X28-30）。
	for i := 5; i < 31; i++ {
		if i == 27 {
			continue
		}
		reg := fmt.Sprintf("X%d", i)
		l.add("MOV", reg, 8)
	}

	// 添加浮点寄存器（F0-F31）。
	for i := 0; i <= 31; i++ {
		reg := fmt.Sprintf("F%d", i)
		l.add("MOVD", reg, 8)
	}

	p("MOV X1, -%d(X2)", l.stack)
	p("ADD $-%d, X2", l.stack)
	l.save()
	p("CALL ·asyncPreempt2(SB)")
	l.restore()
	p("MOV %d(X2), X1", l.stack)
	p("MOV (X2), X31")
	p("ADD $%d, X2", l.stack+8)
	p("JMP (X31)")
}

func genS390X() {
	// 添加整数寄存器R0-R12 
	// R13（g）、R14（LR）、R15（SP）是特殊的，不保存在此处。
	// 不需要保存R10（REGTMP），但无论如何都会保存它。
	var l = layout{sp: "R15", stack: 16} // 添加插槽以保存中断指令和标志的PC 
	l.addSpecial(
		"STMG R0, R12, %d(R15)",
		"LMG %d(R15), R0, R12",
		13*8)
	// 添加浮点寄存器F0-F31。
	for i := 0; i <= 15; i++ {
		reg := fmt.Sprintf("F%d", i)
		l.add("FMOVD", reg, 8)
	}

	// 分配帧，保存中断指令的PC（在LR中）和标志（条件代码）
	p("IPM R10") // 预先保存标志，因为ADD将删除标志
	p("MOVD R14, -%d(R15)", l.stack)
	p("ADD $-%d, R15", l.stack)
	p("MOVW R10, 8(R15)") // 保存标志

	l.save()
	p("CALL ·asyncPreempt2(SB)")
	l.restore()

	p("MOVD %d(R15), R14", l.stack)    // sigctxt。pushCall已在堆栈上推送LR（在中断时），将其还原为
	p("ADD $%d, R15", l.stack+8)       // pop帧（包括SigcText.pushCall推送的空间）
	p("MOVWZ -%d(R15), R10", l.stack)  // 将标志加载到REGTMP 
	p("TMLH R10, $(3<<12)")            // 还原标志
	p("MOVD -%d(R15), R10", l.stack+8) // 将PC加载到REGTMP 
	p("JMP (R10)")
}

func genWasm() {
	p("// wasm上没有异步抢占“）
	p("UNDEF")
}

func notImplemented() {
	p("JMP ·abort(SB)")
}
